// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Global

/* PdfCrossReferenceStream.cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System.Collections.Generic;
using System.Diagnostics;

#endregion

#nullable enable

namespace PdfSharpCore.Pdf.Advanced;

/// <summary>
/// Represents a PDF cross-reference stream.
/// </summary>
internal sealed class PdfCrossReferenceStream
    : PdfTrailer // Reference: 3.4.7  Cross-Reference Streams / Page 106
{
    #region Construction

    /// <summary>
    /// Initializes a new instance of the <see cref="PdfObjectStream"/> class.
    /// </summary>
    public PdfCrossReferenceStream (PdfDocument document)
        : base (document)
    {
        // пустое тело конструктора
    }

    #endregion

    public readonly List<CrossReferenceStreamEntry> Entries = new ();

    [DebuggerDisplay ("{Type} {Field2} {Field3}")]
    public struct CrossReferenceStreamEntry
    {
        // Reference: TABLE 3.16  Entries in a cross-reference stream / Page 109

        public uint Type; // 0, 1, or 2.

        public uint Field2;

        public uint Field3;
    }

    /// <summary>
    /// Predefined keys for cross-reference dictionaries.
    /// </summary>
    public new class Keys
        : PdfTrailer.Keys // Reference: TABLE 3.15  Additional entries specific to a cross-refernece stream dictionary / Page 107
    {
        /// <summary>
        /// (Required) The type of PDF object that this dictionary describes;
        /// must be XRef for a cross-reference stream.
        /// </summary>
        [KeyInfo (KeyType.Name | KeyType.Required, FixedValue = "XRef")]
        public const string Type = "/Type";

        /// <summary>
        /// (Required) The number one greater than the highest object number
        /// used in this section or in any section for which this is an update.
        /// It is equivalent to the Size entry in a trailer dictionary.
        /// </summary>
        [KeyInfo (KeyType.Integer | KeyType.Required)]
        public new const string Size = "/Size";

        /// <summary>
        /// (Optional) An array containing a pair of integers for each subsection in this section.
        /// The first integer is the first object number in the subsection; the second integer
        /// is the number of entries in the subsection.
        /// The array is sorted in ascending order by object number. Subsections cannot overlap;
        /// an object number may have at most one entry in a section.
        /// Default value: [0 Size].
        /// </summary>
        [KeyInfo (KeyType.Array | KeyType.Optional)]
        public const string Index = "/Index";

        /// <summary>
        /// (Present only if the file has more than one cross-reference stream; not meaningful in
        /// hybrid-reference files) The byte offset from the beginning of the file to the beginning
        /// of the previous cross-reference stream. This entry has the same function as the Prev
        /// entry in the trailer dictionary.
        /// </summary>
        [KeyInfo (KeyType.Integer | KeyType.Optional)]
        public new const string Prev = "/Prev";

        /// <summary>
        /// (Required) An array of integers representing the size of the fields in a single
        /// cross-reference entry. The table describes the types of entries and their fields.
        /// For PDF 1.5, W always contains three integers; the value of each integer is the
        /// number of bytes (in the decoded stream) of the corresponding field. For example,
        /// [1 2 1] means that the fields are one byte, two bytes, and one byte, respectively.
        ///
        /// A value of zero for an element in the W array indicates that the corresponding field
        /// is not present in the stream, and the default value is used, if there is one. If the
        /// first element is zero, the type field is not present, and it defaults to type 1.
        ///
        /// The sum of the items is the total length of each entry; it can be used with the
        /// Indexarray to determine the starting position of each subsection.
        ///
        /// Note: Different cross-reference streams in a PDF file may use different values for W.
        ///
        /// Entries in a cross-reference stream.
        ///
        /// TYPE FIELD DESCRIPTION
        ///   0  1  The type of this entry, which must be 0. Type 0 entries define the linked list of free objects (corresponding to f entries in a cross-reference table).
        ///      2  The object number of the next free object.
        ///      3  The generation number to use if this object number is used again.
        ///   1  1  The type of this entry, which must be 1. Type 1 entries define objects that are in use but are not compressed (corresponding to n entries in a cross-reference table).
        ///      2  The byte offset of the object, starting from the beginning of the file.
        ///      3  The generation number of the object. Default value: 0.
        ///   2  1  The type of this entry, which must be 2. Type 2 entries define compressed objects.
        ///      2  The object number of the object stream in which this object is stored. (The generation number of the object stream is implicitly 0.)
        ///      3  The index of this object within the object stream.
        /// </summary>
        [KeyInfo (KeyType.Array | KeyType.Required)]
        public const string W = "/W";

        /// <summary>
        /// Gets the KeysMeta for these keys.
        /// </summary>
        public new static DictionaryMeta Meta
        {
            get { return _meta ??= CreateMeta (typeof (Keys)); }
        }

        private static DictionaryMeta? _meta;
    }

    /// <summary>
    /// Gets the KeysMeta of this dictionary type.
    /// </summary>
    internal override DictionaryMeta Meta => Keys.Meta;
}
